﻿using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;

namespace GBricks
{
  [Pure]
  public static class NullableString
  {
    private static readonly StringComparer CurrentCultureComparer = new NullableStringComparer(StringComparer.CurrentCulture);
    private static readonly StringComparer CurrentCultureIgnoreCaseComparer = new NullableStringComparer(StringComparer.CurrentCultureIgnoreCase);
    private static readonly StringComparer InvariantCultureComparer = new NullableStringComparer(StringComparer.InvariantCulture);
    private static readonly StringComparer InvariantCultureIgnoreCaseComparer = new NullableStringComparer(StringComparer.InvariantCultureIgnoreCase);
    private static readonly StringComparer OrdinalComparer = new NullableStringComparer(StringComparer.Ordinal);
    private static readonly StringComparer OrdinalIgnoreCaseComparer = new NullableStringComparer(StringComparer.OrdinalIgnoreCase);

    #region Comparers

    public static StringComparer CurrentCulture {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<StringComparer>() != null);
        return CurrentCultureComparer;
      }
    }

    public static StringComparer CurrentCultureIgnoreCase {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<StringComparer>() != null);
        return CurrentCultureIgnoreCaseComparer;
      }
    }

    public static StringComparer InvariantCulture {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<StringComparer>() != null);
        return InvariantCultureComparer;
      }
    }

    public static StringComparer InvariantCultureIgnoreCase {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<StringComparer>() != null);
        return InvariantCultureIgnoreCaseComparer;
      }
    }

    public static StringComparer Ordinal {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<StringComparer>() != null);
        return OrdinalComparer;
      }
    }

    public static StringComparer OrdinalIgnoreCase {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<StringComparer>() != null);
        return OrdinalIgnoreCaseComparer;
      }
    }

    #endregion Comparers

    public static StringComparer CreateComparer(StringComparer comparer) {
      Argument.NotNull(comparer, "comparer");
      return new NullableStringComparer(comparer);
    }

    public static StringComparer CreateComparer(CultureInfo culture, bool ignoreCase) {
      var comparer = StringComparer.Create(culture, ignoreCase);
      return CreateComparer(comparer);
    }

    public static bool Equals(string left, string right, StringComparison comparison = StringComparison.Ordinal) {
      if(String.IsNullOrEmpty(left)) {
        return String.IsNullOrEmpty(right);
      } else if(String.IsNullOrEmpty(right)) {
        return false;
      } else {
        return String.Equals(left, right, comparison);
      }//if
    }

    public static int Compare(string left, string right, StringComparison comparison = StringComparison.Ordinal) {
      return String.Compare(left ?? String.Empty, right ?? String.Empty, comparison);
    }

    [Serializable]
    private sealed class NullableStringComparer : StringComparer, IEquatable<NullableStringComparer>
    {
      public NullableStringComparer(StringComparer comparer) {
        Argument.NotNull(comparer, "comparer");
        Comparer = comparer;
      }

      private StringComparer Comparer { get; set; }

      [ContractInvariantMethod]
      private void Invariant() {
        Contract.Invariant(Comparer != null);
      }

      public override bool Equals(string x, string y) {
        if(String.IsNullOrEmpty(x)) {
          return String.IsNullOrEmpty(y);
        } else if(String.IsNullOrEmpty(y)) {
          return false;
        }//if

        return Comparer.Equals(x, y);
      }

      public override int GetHashCode(string obj) {
        if(String.IsNullOrEmpty(obj)) {
          return Comparer.GetHashCode(String.Empty);
        }//if

        return Comparer.GetHashCode(obj);
      }

      public override int Compare(string x, string y) {
        if(String.IsNullOrEmpty(x)) {
          return String.IsNullOrEmpty(y) ? 0 : -1;
        } else if(String.IsNullOrEmpty(y)) {
          return 1;
        }//if

        return Comparer.Compare(x, y);
      }

      public override bool Equals(object obj) {
        return Equals(obj as NullableStringComparer);
      }

      public override int GetHashCode() {
        return Comparer.GetHashCode();
      }

      #region IEquatable<NullableStringComparer> Members

      [Pure]
      public bool Equals(NullableStringComparer other) {
        return other != null && Equals(other.Comparer, Comparer);
      }

      #endregion IEquatable<NullableStringComparer> Members
    }
  }
}
